﻿using Developer.Core.FrameWork.EventBus.Model;
using Developer.Core.FrameWork.EventBus.Service;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Developer.Core.FrameWork.EventBus.SubscriptionManager.MemoryEventBus
{
    /// <summary>
    /// 内存事件总线订阅管理器  单例
    /// </summary>
    public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
    {
        private readonly Dictionary<string, List<SubscriptionInfo>> _handlers;
        public readonly List<Type> _eventTypes;

        public event EventHandler<string> onEventRemoved;

        public InMemoryEventBusSubscriptionsManager()
        {
            this._handlers = new Dictionary<string, List<SubscriptionInfo>>();
            this._eventTypes = new List<Type>();
        }

        /// <summary>
        /// 事件是否为空
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty => !_handlers.Keys.Any();

        /// <summary>
        /// 清除所有
        /// </summary>
        public void Clear() => this._handlers.Clear();

        /// <summary>
        /// 动态订阅
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="eventName"></param>
        public void AddDynamicSubscription<T>(string eventName) where T : IDynamicEventHandler
        {
            this.DoAddSubscription(typeof(T), eventName, true);
        }

        /// <summary>
        /// 订阅
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TH"></typeparam>
        public void AddSubscription<T, TH>()
            where T : EventModel
            where TH : IEventHandler
        {
            var eventName = this.GetEventKey<T>();
            this.DoAddSubscription(typeof(TH), eventName, false);

            if (!this._eventTypes.Contains(typeof(T)))
                this._eventTypes.Add(typeof(T));
        }

        /// <summary>
        /// 移除订阅
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TH"></typeparam>
        public void RemoveSubscription<T, TH>()
            where T : EventModel
            where TH : IEventHandler<T>
        {
            var handlerToRemove = this.FindSubscriptionToRemove<T, TH>();

            var eventName = this.GetEventKey<T>();

            this.DoRemoveHandler(eventName, handlerToRemove);
        }

        /// <summary>
        /// 移除订阅  泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void RemoveDynamicSubscription<T>(string eventName) where T : IDynamicEventHandler
        {
            var handler = this.FindDynamicSubscriptionToRemove<T>(eventName);

            this.DoRemoveHandler(eventName, handler);
        }

        /// <summary>
        /// 事件是否存在订阅  泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="eventName"></param>
        /// <returns></returns>
        public bool HasSubscriptionForEvent<T>() where T : EventModel
        {
            var key = this.GetEventKey<T>();

            return this.HasSubscriptionForEvent(key);
        }

        /// <summary>
        /// 事件是否存在订阅 
        /// </summary>
        /// <param name="eventName"></param>
        /// <returns></returns>
        public bool HasSubscriptionForEvent(string eventName) => this._handlers.ContainsKey(eventName);

        /// <summary>
        /// 通过名称获取事件类型
        /// </summary>
        /// <param name="eventName"></param>
        /// <returns></returns>
        public Type GetEventTypeByName(string eventName)
        {
            return this._eventTypes.SingleOrDefault(x => x.Name == eventName);
        }

        /// <summary>
        /// 获取订阅事件的处理事件  泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<SubscriptionInfo> GetHandlerForEvent<T>() where T : EventModel
        {
            var type = this.GetEventKey<T>();

            return this.GetHandlerForEvent(type);
        }

        /// <summary>
        /// 获取订阅事件的处理事件
        /// </summary>
        /// <param name="eventName"></param>
        /// <returns></returns>
        public IEnumerable<SubscriptionInfo> GetHandlerForEvent(string eventName) => this._handlers[eventName];

        /// <summary>
        /// 获取事件名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public string GetEventKey<T>()
        {
            return typeof(T).Name;
        }


        public void DoAddSubscription(Type handlerType, string eventName, bool isDynamic)
        {
            if (!this.HasSubscriptionForEvent(eventName))
                this._handlers.Add(eventName, new List<SubscriptionInfo>());

            if (this._handlers[eventName].Any(x => x.HandlerType == handlerType))
                throw new ArgumentException($"Handler Type {handlerType.Name} already registered for '{eventName}'", nameof(handlerType));

            if (isDynamic)
                this._handlers[eventName].Add(SubscriptionInfo.Dynamic(handlerType));
            else
                this._handlers[eventName].Add(SubscriptionInfo.Typed(handlerType));
        }

        private SubscriptionInfo FindSubscriptionToRemove<T, TH>()
            where T : EventModel
            where TH : IEventHandler<T>
        {
            var eventName = GetEventKey<T>();
            return DoFindSubscriptionToRemove(eventName, typeof(TH));
        }

        public SubscriptionInfo DoFindSubscriptionToRemove(string eventName, Type handlerType)
        {
            if (!HasSubscriptionForEvent(eventName))
                return null;

            return this._handlers[eventName].SingleOrDefault(s => s.HandlerType == handlerType);
        }

        private void DoRemoveHandler(string eventName, SubscriptionInfo subscriptionInfo)
        {
            if (subscriptionInfo != null)
            {
                this._handlers[eventName].Remove(subscriptionInfo);

                if (!this._handlers[eventName].Any())
                {
                    this._handlers.Remove(eventName);

                    var eventType = this._eventTypes.SingleOrDefault(x => x.Name == eventName);

                    if (eventType != null)
                    {
                        this._eventTypes.Remove(eventType);
                    }

                    RaiseOnEventRemoved(eventName);
                }
            }
        }

        private void RaiseOnEventRemoved(string eventName)
        {
            var handler = onEventRemoved;
            handler?.Invoke(this, eventName);
        }

        private SubscriptionInfo FindDynamicSubscriptionToRemove<TH>(string eventName)
            where TH : IDynamicEventHandler
        {
            return DoFindSubscriptionToRemove(eventName, typeof(TH));
        }
    }
}
